home *** CD-ROM | disk | FTP | other *** search
/ Mac Mania 6 / MacMania 6.toast / / Multimedia & Desktop / VideoToolbox / VideoToolboxSources / GDOpenWindow.c < prev    next >
Text File  |  1997-07-02  |  25KB  |  517 lines

  1. /* GDOpenWindow.c
  2. Copyright © 1989-1997 Denis G. Pelli, David H. Brainard
  3.  
  4. EXAMPLE:
  5.  
  6. Open your window by saying:
  7.     window=GDOpenWindow(device);
  8. Show it by saying:
  9.     ShowWindow((WindowPtr)window);
  10. When you're through with the window, get rid of it by calling:
  11.     GDDisposeWindow(window);
  12. Besides closing the window and disposing of the allocated memory structures
  13. GDDisposeWindow also restores the device's color table and clut.
  14.  
  15. SUMMARY:
  16.  
  17. window=GDOpenWindow(device) opens a full-screen window on the specified screen,
  18. calls GDGrayColorTable(device) which gives the device a standard gray color table,
  19. and calls AddExplicitPalette(window), which allows you
  20. to specify colors to paint with by using PmForeColor() and PmBackColor(). The
  21. window truly fills the screen: any rounded corners are made square and if the
  22. window is on the main screen then the Menu Bar is hidden. The screen occupies
  23. the content area of the window; the window's frame (one-pixel black line border
  24. and title bar) is off that screen, but will appear on other screens that form
  25. contiguous parts of your desktop. The elegant solution to this, which perhaps
  26. someone will implement, would be to define a new window type that has no frame.
  27.  
  28. GDDisposeWindow(window) closes and discards a color window, disposing of any
  29. palette or color table, restoring the screen, generally undoing whatever GDOpenWindow1 did.
  30.  
  31. AddExplicitPalette(window) adds a palette to a color window or GWorld with all
  32. the colors marked as pmExplicit. This allows you to use PmForeColor() and
  33. PmBackColor() to specify the value you want poked into each pixel by QuickDraw
  34. operations, e.g. EraseRect() and DrawString().
  35.  
  36. RemovePalette(window) disposes of the palette created by AddExplicitPalette.
  37.  
  38. SwapWindowExplicitMode(window,&isExplicit) swaps the contents of the Boolean
  39. argument with the "explicit" bit (bit 14 of the ctFlags field) in the color
  40. table. The use of this bit is documented in 
  41. <http://rajsky.psych.nyu.edu/VideoToolbox/VideoNotes/CopyBitsTip.html>. 
  42. After much experimentation I decided that it's not useful, but I
  43. leave this routine here for others that wish to experiment. Note that when
  44. GDOpenWindow() creates a window, the window shares the color table belonging to
  45. the device, so calling SwapWindowExplicitMode() modifies the shared table, i.e.
  46. the device's color table is modified. You should restore it by calling
  47. MakeColorTableExplicit() again. Failing to restore it will have cosmetic
  48. aftereffects, even after your application quits. The most obvious aftereffect is
  49. that documents in Word will be largely gray.
  50.  
  51. GetBitMapPtr(window) returns a pointer to the window's bitmap or pixmap.
  52.  
  53. IsGWorldPtr(window) returns true only if the window is a GWorldPtr.
  54.  
  55. COMMENTS:
  56.  
  57. Please read <http://rajsky.psych.nyu.edu/VideoToolbox/VideoNotes/CopyBitsTip.html>.
  58. QuickDraw likes to
  59. pick a bunch of good colors and stuff them in the clut, in essentially random
  60. order, except that white is first and black is last. It wants you to specify any
  61. desired color as an RGB triplet and then it picks the mysterious clut index that
  62. would provide the closest match. If you're processing grayscale images, then
  63. QuickDraw's approach involves a lot of overhead involving inverse color tables,
  64. and makes the numbers stored in your pixels meaningless (unless you look them up
  65. in the associated color table or palette).
  66.  
  67. The philosophy of the VideoToolbox is to bypass QuickDraw's color model, and
  68. work explicitly with the numbers that are stored in your pixels.
  69. SetPixelsQuickly will efficiently poke (or peek) the numbers in your PixMap.
  70. However, if you want to use QuickDraw's handy drawing operations, especially
  71. EraseRect() and DrawString() then you need a way to specify the foreground and
  72. background colors. AddExplicitPalette() gives your window a palette in which all
  73. the colors are marked as pmExplicit. This tells the palette manager to use your
  74. arguments to PmForeColor() and PmBackColor() literally.
  75.  
  76. I suggest that you control the clut by calling GDUncorrectedGamma() and
  77. GDSetEntries(), since these calls directly control the video device driver,
  78. bypassing the Color Manager. Since the Color and Palette Managers don't know
  79. that you've changed the color environment they can't react to it, and will
  80. passively let you continue to specify colors by their clut index. For example,
  81. the Palette Manager religiously believes that the first clut entry should be
  82. white and the last one black, and it will change them back to those values if
  83. you change them and it finds out about it. If you use the Color Manager
  84. SetEntries call then the Paletter Manger WILL find out because a record is made
  85. in the ColorTable. Calling GDSetEntries() bypasses the Color Manager. Instead,
  86. the video device driver writes directly to the clut and the ColorTable is not
  87. modified. Of course, this means that you should ignore the ColorTable since it
  88. will no longer reflect the contents of the clut.
  89.  
  90. Every time you access the stdio package, e.g. printf or getch(), THINK C will
  91. move the console window to the front, which may obscure your window. You can
  92. bring your window back to the front by calling BringToFront() or SelectWindwo().
  93.  
  94. NOTE:
  95. Tom Busey (busey@indiana.edu) writes: "I'm using an 8-bit SuperMac ColorCard
  96. that a used computer dealer gave me when I ordered a Toby card. I discovered
  97. that the status and control calls for GDUncorrectedGamma return -17 and -18
  98. respectively, which means that the driver doesn't support different gammas.
  99. GDUncorrectedGamma returns an error message, but GDOpenWindow doesn't use the
  100. error message or pass it back to the calling program. I'm wondering if there are
  101. people using GDOpenWindow who think that they are getting a linear gamma but who
  102. are actually getting an error message and not learning about it."
  103.      Tom's facts are right, but he needn't worry. A few video drivers, e.g.
  104. Radius PowerView, and apparently the SuperMac ColorCard, don't implement gamma
  105. tables at all, but the error from GDUncorrectedGamma is probably not a concern.
  106. The real test is to run TimeVideo. TimeVideo does a write-and-read-back test of
  107. the clut of your video card. If that test passes then you can safely conclude
  108. that there's no gamma translation going on. So, as nearly every document in the
  109. VideoToolbox says: run TimeVideo and read the report.
  110.      Here's how gamma tables work. According to the Apple manual (Designing
  111. Cards and Drivers), the values in the rgb triplets that you supply in a
  112. cscSetEntries call to the video driver are first translated via the gamma table
  113. (each r,g, and b component separately) before being loaded into the CLUT
  114. hardware table. Most drivers maintain a gamma table internally (in computer
  115. memory) and allow you to get and set it via the GDGetGamma and GDSetGamma calls.
  116. A few video drivers (Radius PowerView, SuperMac ColorCard) don't support gamma
  117. tables. They load your rgb values directly into the CLUT, without translation.
  118. However, for users of the VideoToolbox that's usually fine, since that's exactly
  119. the behavior that we routinely request by calling GDUncorrectedGamma. Of course,
  120. if you want to load a custom gamma table, other than the identity
  121. transformation, then you'll be calling GDSetGamma, and you should make sure it
  122. does not return an error. (Alas, the Radius PowerView driver more or less
  123. ignores the set- and get-gamma requests--GDGetGamma returns a table that's all
  124. zeroes--but the driver fails to report an error. I wrote to the Radius
  125. programmer a year ago, but they're no longer interested in working on that
  126. product.)
  127.      
  128.  
  129. PROBLEM:
  130. How can I tell whether I've got a CGrafPtr or a GWorldPtr? (In one case I'll call 
  131. GetGWorldDevice, in the other case I'll get max device.) I noticed that the 
  132. portVersions are 0xc001 and 0xc000. Should I use that to tell?
  133.  
  134. ANSWER FROM APPLE DEVSUPPORT (8/93):
  135. You can use GetGWorldDevice() on GWorldPtr, GrafPort or CGrafPort. Use
  136. TestDeviceAttibute() to test if the returned GDHandle is a "real" screen or not.
  137.  
  138. Also, you should refer to the tech note “RowBytes Revealed II” (available on the
  139. June Developer CD) which states:
  140.  
  141. You'll notice in Figure 1 that the portVersion field of a cGrafPort coincides
  142. with the location of the rowBytes field of a grafPort. Remember, a cGrafPort has
  143. the same size as a grafPort. During debugging, you can use the same information
  144. that CopyBits uses to identify cGrafPorts.
  145.  
  146. If you use a grafPort template to display memory for an unknown grafPort, you
  147. can tell if it is a cGrafPort because the rowBytes will be equal to 0xC000. The
  148. 0xC corresponds to the two high bits being set in the portVersion field of a
  149. cGrafPort. Since these bits can not be set in a grafPort, you know you have a
  150. cGrafPort. In addition, if the bottom bit of the portVersion field is set, then
  151. it is a gWorld. Thus, if your rowBytes field has a value of 0xC001, then you
  152. know you have a gWorld.
  153.  
  154. CONCLUSION:
  155. This advice is implemented in the routine IsGWorldPtr(window).
  156.  
  157. REFERENCES:
  158. http://devworld.apple.com/dev/technotes/tn/tn1024.html
  159. http://devworld.apple.com/dev/techsupport/source/code/Snippets/QuickDraw/Restore_Screen_Cluts/ReadMe.html
  160.  
  161. HISTORY:
  162.  
  163. 12/88        dgp    wrote it
  164. 8/5/89            added call to GDUncorrectedGamma, so I couldn't forget.
  165. 8/15/89     dgp trivia
  166. 3/20/90        dgp    make compatible with MPW C.
  167. 3/29/90        dgp    changed declared returned type from WindowPtr to CWindowPtr, which
  168.                 is what it's really been all along. Same change to argument of
  169.                 GDDisposeWindow(). The new offscreen GWorld calls for the first time
  170.                 make it easier to honestly declare one's windows as color rather
  171.                 than pretending they're not.
  172. 8/24/91        dgp    Made compatible with THINK C 5.0.
  173. 2/1/92        dgp Made optional the device argument to GDDisposeWindow(). If it's
  174.                 NULL, then it will be determined automatically from window.
  175. 2/3/93        dhb    Extracted AddExplicitPalette from GDOpenWindow.
  176. 2/21/93        dgp    HideMenuBar if window is on main screen.
  177. 2/23/93        dgp AddExplicitPalette() returns immediately unless it receives a color 
  178.                 window.
  179.                 Added GDOpenWindow1() and GDDisposeWindow1(), which both use
  180.                 a WindowPtr, instead of a less convenient CWindowPtr.
  181. 3/5/93        dgp    Added calls to UnclipScreen() and RestoreScreenClipping(), so the window
  182.                 now truly fills the whole screen. Edited GDOpenWindow() for clarity.
  183. 3/7/83        dgp    Added calls to GDSaveGamma(device) and GDRestoreGamma(device).
  184. 4/16/93        dgp    Cosmetic editing.
  185. 5/22/93        dgp    Added RemovePalette(), but didn't test it.
  186. 1/27/94        dgp    Cosmetic editing.
  187. 3/5/94        dgp    Decided that RemovePalette() works fine now.
  188. 6/8/94        dgp AddExplicitPalette now calls the new MakeColorTableExplicit(), which
  189.                 sets the Color Table ctFlags to indicate that the Color Table refers 
  190.                 explicitly to the explicit palette. This tells CopyBits to treat
  191.                 the pixels as numbers, not indices.
  192. 6/13/94        dgp In response to query by David Brainard, I now document above
  193.                 the obscure fact that GDOpenWindow's frame will appear on
  194.                 other screens that are contiguous parts of the desktop.
  195. 8/11/94 dhb & dgp David Brainard discovered that MakeColorTableExplicit(), which
  196.                 is called by AddExplicitPalette, which is called by GDOpenWindow1, was affecting
  197.                 not just the new window's color table, but was also affecting the device's color
  198.                 table (because they share the same color table), and that the device's color
  199.                 table was not restored. "The symptom is that if you subsequently launch
  200.                 Microsoft Word, the background of the text window takes on a strange color
  201.                 (grey) in regions where there is text," and the problem persists until reboot.
  202.                 The solution is to give our window its own color table, which we can
  203.                 safely modify. 
  204. 8/12/94 dhb & dgp on 8/11 denis inadvertently introduced a bug into GDDisposeWindow1, 
  205.                 moving the call to GetWindowDevice() to a point after the window
  206.                 was disposed of. David fixed it, moving the call back where it belonged.
  207. 8/14/94        dgp    GDOpenWindow1 now gets the color table from the window's device instead
  208.                 of the main device.
  209. 8/17/94        dgp make color table NOT explicit when GDDisposeWindow1 is called.
  210. 9/5/94         dgp removed assumption in printf's that int==short.
  211. 10/10/94    dgp GDOpenWindow1 now associates correct device's color table with window,
  212.                 not MainDevice's.
  213. 10/24/94    dgp GDOpenWindow1 returns NULL if there's no driver associated with the device.
  214. 10/25/94    dgp Added GDGrayColorTable(device), which gives the device a standard gray color table.
  215. 11/2/94        dgp Leave the explicit bit alone. For the adventurous I provide the 
  216.                 MakeColorTableExplicit() routine and documentation in 
  217.                 <http://rajsky.psych.nyu.edu/VideoToolbox/VideoNotes/CopyBitsTip.html>
  218. 11/17/94    dgp    renamed IsWindow to IsGrafPtr. Renamed IsOffScreen to IsGWorldPtr.
  219. 4/11/95        dgp Fixed assertion failure in GDGrayWindow; the rsrc id of the 2-bit gray ramp is 2,
  220.                 not 32+2.
  221. 4/11/95        dgp    transferred some documentation of IsGWorldPtr from GetScreenDevice.c to above.
  222. 3/8/95        dgp    GDOpenWindow1()'s call to GDGrayColorTable() causes the 
  223.                 window frame to become orange. I don't understand why, but it's only a cosmetic bug, 
  224.                 so i'm ignoring it for the moment.
  225. 10/23/96     dhb Added code to set the entries in the explicit palette added by AddExplicitPalette().
  226.                 This is doing the same thing that the MAKE_COLOR_TABLE conditional was trying to do,
  227.                 but it seems to work. I got the example (sort of) out of the Advanced Color Imaging
  228.                 manual, p. 1-26. A brief test indicates that the new code makes things work properly in 
  229.                 32-bit mode and doesn't break them in 8-bit mode.
  230. 10/28/96    dhb Fixed bug introduced by 10/23/96 change. With MAKE_COLOR_TABLE set to 2, the screen did
  231.                 not restore properly on exit. I changed code to create a new color table in mode 2 as well
  232.                 as in mode 1. This seems to allow the proper restore on close.
  233. 11/14/96    dgp renamed argument of SwapWindowExplicitMode from "explicit" to "isExplicit" to 
  234.                 avoid conflict with the C++ reserved word "explicit". Thanks to  Eric Fredericksen, 
  235.                 eric@mach2.hipl.uci.edu for alerting me to the problem and solution.
  236. 12/18/96    dhb Modified MAKE_COLOR_TABLE==2 code to only operate if pixelSize>8. This makes
  237.                 the behavior the same as the old default for less than 8 bits/pixel but like
  238.                 the new code for more than 8 bits/pixel. I did this because the new method
  239.                 introduced a subtle bug in 8 bit/pixel mode, wherein when you wrote 0 to
  240.                 the frame buffer through the various draw routines the value was sent as 255.
  241. 12/18/96    dgp Changed i<<8, which assumes 8-bit DAC, to (i<<8)+i which works for any DAC size.
  242. 12/18/96    dgp Eliminated the old MAKE_COLOR_TABLE==1 mode, which no one ever used, reducing the
  243.                 choice to false (i.e. 0), the old, pre 10/96 code, and true (i.e. 1), for
  244.                 David's new code, which makes a color table only for more-than-8-bit pixels.
  245. 1/20/97        dgp Generalized GDGrayColorTable to become GDStandardColorTable.
  246. 1/20/97        dgp Fixed long-standing bug in GDDisposeWindow: it failed to restore the CLUT. It
  247.                 now calls GDStandardColorTable and GDRestoreDeviceCLUT, to restore a standard
  248.                 color table, and following the helpful RestoreColorSlam.c snippet by Apple's 
  249.                 Forrest Tanaka (see REFERENCES, above), we call DrawBehind() to redraw the
  250.                 screen in the new color table.
  251. 2/1/97        dgp    GDDisposeWindow now is more successful at restoring the screen. I changed 
  252.                 PaintBehind(NULL,GetGrayRgn()) to PaintBehind(FirstWindow(),rgn). Previously
  253.                 the front window wasn't getting updated; now it is. Replacing GetGrayRgn() 
  254.                 by rgn (the structure region of the window we're closing) restricts the update
  255.                 to just the affected screen, which makes the update faster and less visually
  256.                 distracting when there are multiple screens.
  257. 2/2/97        dgp    Cleaned up the allocation and disposal of the rgn mentioned immediately above.
  258. 2/2/97        dgp    GDOpenWindow now calls RestoreDeviceClut to actually copy the new color table
  259.                 into the device's CLUT. And we call SelectWindow on our new window to make the
  260.                 Palette Manager aware. As a result, GDOpenWindow now seems to correctly
  261.                 leave us with a grayscale clut, and a window whose palette matches that clut.
  262. 3/26/97        dgp Fixed bug in IsGWorldPtr(). It was testing whether 3 bits were on, whereas it
  263.                 ought to have been testing that the value of the word was 0xc001, i.e. also making
  264.                 sure that the rest of the bits were zero. This may have been responsible
  265.                 for some mysterious crashes of 68K code.
  266. 3/26/97        dgp Added window!=NULL test to both IsGWorldPtr and IsGrafPtr.
  267. 3/27/97        dgp Added (unsigned short) to IsGWorldPtr for correct comparison.
  268. 3/29/97        dgp Following advice of Inside Mac, GDOpenWindow now leaves new window invisible.
  269.                 User must call ShowWindow to reveal it.
  270. 5/19/97        dgp Added URLs to documents now in website VideoNotes folder.
  271.  
  272. KNOWN BUGS:
  273. 2/2/97 dgp The window created by GDOpenWindow fills the screen of the specified device, but
  274. has a one-pixel frame and a title bar that are just off that screen, and will appear on the
  275. edges of other screens that are logically adjacent on the desktop. I think this could be
  276. fixed by defining a new window type that had no frame.
  277. 2/2/97 dgp We never update the window. If you move another window in front of it, and then
  278. move ours back to front, there will be a big white footprint where the other window used to
  279. be. Fixing this would require saving a copy (at all times?) of our window in an offscreen
  280. GWorld and copying from there to restore any invalidated part when our window receives
  281. at update event. (At present we ask the OS not to pass any update events to our window.)
  282. */
  283. #include "VideoToolbox.h"
  284.  
  285. // Set MAKE_COLOR_TABLE to 0 (i.e. false) to generate the old code, pre 10/96, that only
  286. // supports up-to-8-bit pixels. Set it to 1 (i.e. true) to get DHB's enhanced version of
  287. // the color table code that supports all pixels sizes. See 10/23/96, 10/28/96 history
  288. // comments above. We suggest you leave it set to 1 unless you discover
  289. // that this causes a problem with large pixels that you didn't formerly have.
  290. // If you do find a problem, please let us know: denis@cns.nyu.edu, brainard@psych.ucsb.edu
  291. #define MAKE_COLOR_TABLE 1
  292.  
  293. void GDStandardColorTable(GDHandle device,Boolean isColor);    // This can be deleted once VideoToolbox.h is precompiled anew.
  294.  
  295. WindowPtr GDOpenWindow1(GDHandle device)
  296. {
  297.     return (WindowPtr)GDOpenWindow(device);
  298. }
  299.  
  300. void GDDisposeWindow1(WindowPtr window)
  301. {
  302.     GDDisposeWindow((CWindowPtr) window);
  303. }
  304.  
  305. CWindowPtr GDOpenWindow(GDHandle device)
  306. {
  307.     CWindowPtr window;
  308.     Rect r;
  309.     GDHandle oldDevice;
  310.  
  311.     if(device==NULL || (*device)->gdRefNum==0)return NULL;
  312.     r=(*device)->gdRect;        // rect of desired screen in global coordinates
  313.     GDSaveGamma(device);
  314.     GDUncorrectedGamma(device);
  315.     UnclipScreen(device);
  316.     GDGrayColorTable(device);
  317.     oldDevice=GetGDevice();
  318.     SetGDevice(GetMainDevice());    // Needed for window to work properly.
  319.     window=(CWindowPtr)NewCWindow(NULL,&r,"\p",FALSE,plainDBox,(WindowPtr) -1L,0,123L);
  320.     (*window->portPixMap)->pmTable=(**(**device).gdPMap).pmTable;
  321.     SetGDevice(oldDevice);
  322.     AddExplicitPalette((WindowPtr)window);
  323.     RestoreDeviceClut(device);        // Copy new color table into CLUT
  324.     SelectWindow((WindowPtr)window);
  325.     return window;
  326. }
  327.  
  328. void GDGrayColorTable(GDHandle device)
  329. // Give the device a standard gray-ramp color table, appropriate to its pixelSize
  330. {
  331.     GDStandardColorTable(device,0);
  332. }
  333.  
  334. void GDStandardColorTable(GDHandle device,Boolean isColor)
  335. // Give the device a standard color (if isColor) or grayscale (if !isColor) color table, 
  336. // appropriate to its pixelSize
  337. {
  338.     ColorTable **table;
  339.     PixMap **pm;
  340.     int id;
  341.  
  342.     if(device==NULL)return;
  343.     pm=(**device).gdPMap;
  344.     table=(**pm).pmTable;
  345.     id=(**pm).pixelSize;
  346.     // As explained in Advanced Color Imaging, +64 requests a color table with the hilight color.
  347.     if(isColor)id+=64;    // color
  348.     else id+=32;        // grayscale
  349.     if((**device).gdType==clutType && (**table).ctSeed!=id){
  350.         table=GetCTable(id);
  351.         assert(table!=NULL);
  352.         assert((**(**pm).pmTable).ctSize==(**table).ctSize);
  353.         memcpy(*(**pm).pmTable,*table,8+((**table).ctSize+1)*sizeof(ColorSpec));
  354.         DisposeCTable(table);
  355.         table=(**pm).pmTable;
  356.         (**table).ctFlags|=0x8000;    // mark as belonging to a device
  357.         GDeviceChanged(device);
  358.     }
  359. }
  360.  
  361. void GDDisposeWindow(CWindowPtr window)
  362. // Dispose of window and palette and restore the screen's clut and clipping.
  363. {
  364.     GDHandle device;
  365.     Boolean isColor;
  366.     Region **rgn;
  367.     
  368.     if(window==NULL)return;
  369.     RemovePalette((WindowPtr)window);
  370.     device=GetWindowDevice((WindowPtr)window);
  371.     if(device!=NULL){
  372.         // Restore CLUT before closing window, so screen update will look right
  373.         GDRestoreGamma(device);                    // restore original gamma table
  374.         isColor=TestDeviceAttribute(device,gdDevType);
  375.         GDStandardColorTable(device,isColor);    // install standard color table
  376.         GDRestoreDeviceClut(device);            // copy color table into CLUT
  377.     }
  378.     #if MAKE_COLOR_TABLE
  379.         if((**((CGrafPtr)window)->portPixMap).pixelSize>8)
  380.             DisposeHandle((Handle)(*((CGrafPtr)window)->portPixMap)->pmTable);
  381.     #endif
  382.     DisposeWindow((WindowPtr)window);
  383.     if(device!=NULL){
  384.         RestoreScreenClipping(device);    // calls DrawMenuBar().
  385.         rgn=NewRgn();
  386.         RectRgn(rgn,&(*device)->gdRect);                // rect of screen in global coordinates
  387.         PaintBehind(FrontWindow(),rgn);                    // redraw that screen with new color tables
  388.         DisposeRgn(rgn);
  389.     }
  390. }
  391.  
  392. void AddExplicitPalette(WindowPtr window)
  393. // Create a palette for the color window and mark all the entries as explicit.
  394. // Copy the entries from the window's device.
  395. {
  396.     GDHandle device;
  397.     CTabHandle ct;
  398.     PaletteHandle palette;
  399.     int colors;
  400.     int pixelSize;
  401.     #if MAKE_COLOR_TABLE
  402.         int i,error;
  403.         RGBColor color;
  404.     #endif
  405.  
  406.     if(window==NULL)return;
  407.     if(((CGrafPtr)window)->portVersion>=0) return;    // Not a color window, return.
  408.     device=GetWindowDevice(window);
  409.     if(device==NULL)PrintfExit(
  410.         "AddExplicitPalette: window has no device! %s line %d.\n",__FILE__,__LINE__);
  411.     ct=(**(**device).gdPMap).pmTable;
  412.     colors=(*ct)->ctSize+1;
  413.     pixelSize = (**(**device).gdPMap).pixelSize;
  414.     #if MAKE_COLOR_TABLE
  415.         if (pixelSize>8) {
  416.             error=HandToHand((Handle *)&ct);
  417.             if(error)PrintfExit("%s line %d: error %d in copying color table\n"
  418.                 ,__FILE__,__LINE__,error);
  419.             (*ct)->ctFlags &= 0x7fff;    // clear "device" bit
  420.             for(i=0;i<colors;i++) (*ct)->ctTable[i].value=i;
  421.             (*ct)->ctSeed=GetCTSeed();
  422.             (*((CGrafPtr)window)->portPixMap)->pmTable=ct;
  423.         }
  424.     #endif
  425.     palette=NewPalette(colors,ct,pmExplicit+pmTolerant,0);
  426.     #if MAKE_COLOR_TABLE
  427.         if (pixelSize>8) {
  428.             for (i = 0; i<colors; i++) {
  429.                 color.red = color.green = color.blue = (i<<8)+i;
  430.                 SetEntryColor(palette,i,&color);
  431.             }
  432.         }
  433.     #endif
  434.     SetPalette(window,palette,0);
  435. }
  436.  
  437. /*
  438. RemovePalette may look dangerous, but it isn't. I worried that the Window or
  439. Palette Manager might become confused if it later tries to access the
  440. window's palette, which no longer exists. However, by trial and error
  441. I've found that this works fine, and that my attempts to make explicit the
  442. fact that the palette is gone, e.g. by calling SetPalette(window,NULL,0),
  443. all produced crashes. I conclude that the Palette or Window Manager monitors
  444. the calls to DisposePalette and thus knows that the palette is gone.
  445. */
  446. void RemovePalette(WindowPtr window)
  447. {
  448.     if(window==NULL)return;
  449.     DisposePalette(GetPalette(window));
  450. }
  451.  
  452. void SwapWindowExplicitMode(CWindowPtr window,Boolean *isExplicit)
  453. // Get and set bit 14 of the ctFlags field of the window's color table.
  454. // This is only relevant to a CopyBits from an offscreen
  455. // graphics environment, be it PixMap or GWorld, to an onscreen window.
  456. // See Inside Mac VI, page 20-17. To get the full story see 
  457. // <http://rajsky.psych.nyu.edu/VideoToolbox/VideoNotes/CopyBitsTip.html>
  458. {
  459.     int i,ok;
  460.     ColorTable **ct;
  461.     Boolean oldIsExplicit;
  462.  
  463.     if(window==NULL)return;
  464.     // If it's a GWorldPtr then we must lock the pixels to access the pixmap.
  465.     if((window->portVersion&0xc001)==0xc001){
  466.         ok=LockPixels(GetGWorldPixMap(window));
  467.         if(!ok)return;
  468.     }
  469.     ct=(*window->portPixMap)->pmTable;
  470.     oldIsExplicit=((*ct)->ctFlags&0x4000)!=0;
  471.     if(*isExplicit){
  472.         (*ct)->ctFlags|=0x4000;
  473.         for(i=(*ct)->ctSize;i>=0;i--)(*ct)->ctTable[i].value=i;
  474.     }else{
  475.         (*ct)->ctFlags&=~0x4000;
  476.     }
  477.     *isExplicit=oldIsExplicit;
  478. }
  479.  
  480. // CAUTION: The PixMap handles are not locked. So don't move memory. The
  481. // returned pointers will become invalid as soon as you call any routine
  482. // that moves memory. CopyBits and CopyBitsQuickly are ok.
  483. BitMap *GetBitMapPtr(CWindowPtr window)
  484. {
  485.     BitMap *bits;
  486.     int ok;
  487.     PixMapPtr *pm;
  488.  
  489.     if(IsGWorldPtr(window)){
  490.         // It's a GWorldPtr, lock the pixels, return pixmap ptr.
  491.         ok=LockPixels(GetGWorldPixMap((GWorldPtr)window));
  492.         if(!ok)return NULL;
  493.         pm=GetGWorldPixMap((GWorldPtr)window);
  494. //        HLock((Handle)pm);
  495.         bits = (BitMap *)*pm;
  496.     }else if(!IsGrafPtr(window)){
  497.         pm=window->portPixMap;
  498. //        HLock((Handle)pm);
  499.         bits = (BitMap *)*pm;
  500.     }else{
  501.         bits = &((WindowPtr)window)->portBits;
  502.     }
  503.     return bits;
  504. }
  505.  
  506. Boolean IsGWorldPtr(CWindowPtr window)    // Is it a GWorldPtr?
  507. {
  508.     return window!=NULL && (unsigned short)window->portVersion==(unsigned short)0xc001;
  509. }
  510.  
  511.  
  512. Boolean IsGrafPtr(CWindowPtr window)    // Is it a WindowPtr?
  513. {
  514.     return window!=NULL && window->portVersion>=0;
  515. }
  516.  
  517.